package gdt.service;

import gdt.dao.MemberDAO;
import gdt.entity.Group;
import gdt.entity.Member;
import gdt.service.utils.FileUpload;
import gdt.service.utils.Validator;

import java.util.List;

import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.providers.dao.SaltSource;
import org.springframework.security.providers.encoding.PasswordEncoder;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


public class MemberService {

	@Transactional(readOnly=true)
	public Member findById(long id){
		return memberDao.findById(id);
	}
	
	public Member findByUsername(String username){
		List<Member> users = memberDao.findByUsername(username);
		if(!users.isEmpty())
			return memberDao.findByUsername(username).get(0);
		else
			throw new UsernameNotFoundException(username + "not found");
	}
	
	@Transactional(readOnly=true)
	public List<Member> findByUser(String username){
		Member user = (Member) userDetailsService.loadUserByUsername(username);
		
		return user.getGroup().getMembers();
	}
	
	@Transactional(propagation=Propagation.REQUIRED,readOnly=false)
	public boolean save(Member member, boolean admin, MessageContext messageContext){
		if(validate(member,messageContext,null,null)){
			member = save(member);
		
			member = save(member,admin,member.getPassword());
			return true;
		}
		else{
			return false;
		}
	}
	
	@Transactional(propagation=Propagation.REQUIRED,readOnly=false)
	public Member save(Member member, boolean admin, String password){
		if(admin)
			member.addAuthority("ROLE_ADMIN");
		else
			member.removeAuthority("ROLE_ADMIN");
		
		if(password != null && !password.equals("")){
			changePassword(member, password);
		}
		
		member = memberDao.makePersistent(member);
		return member;
	}
	
	@Transactional(propagation=Propagation.REQUIRED,readOnly=false)
	public Member save(Member member){
		member = memberDao.makePersistent(member);
		return member;
	}
	
	public void changePassword(Member member, String password){
		Object salt = saltSource.getSalt(member);
		member.setPassword(passwordEncoder.encodePassword(password, salt));
	}
	
	public void changeMembersPassword(Group group){
		for(Member member : group.getMembers()){
			changePassword(member, member.getPassword());
			save(member);
		}	
	}
	
	
	@Transactional(propagation=Propagation.REQUIRED,readOnly=false)
	public boolean delete(Member member, MessageContext messageContext){
		if(member.getAssignedProjects().isEmpty()){
			member.getGroup().removeMember(member);
			memberDao.makeTransient(member);
			return true;
		}else{
			messageContext.addMessage((new MessageBuilder()).error().code("error_member_delete").build());
			return false;
		}
	}
	
	@Transactional(readOnly=true)
	public Member setup(String username){
		Member m = new Member();
		Member user = (Member) userDetailsService.loadUserByUsername(username);
		m.setGroup(user.getGroup());
		m.addAuthority("ROLE_USER");

		return m;
	}
	
	public Member setup(Group group){
		Member m = new Member();
		m.addAuthority("ROLE_USER");
		m.addAuthority("ROLE_ADMIN");
		group.addMember(m);

		return m;
	}
	
	
	public boolean isAdmin(Member member){
		for(GrantedAuthority authority : member.getAuthorities()){
			if(authority.getAuthority().equals("ROLE_ADMIN"))
				return true;
		}
		return false;
	}
	
	
	public FileUpload getImageFileUpload(Member member){
		FileUpload fileUpload = new FileUpload();
		fileUpload.setMember(member);
		return fileUpload;
	}
	


	
	/********************************* DAO's *********************************/
	
	private UserDetailsService userDetailsService;

	public void setUserDetailsService(UserDetailsService userDetailsService) {
		this.userDetailsService = userDetailsService;
	}
	
	private MemberDAO memberDao;
	
	
	public void setMemberDao(MemberDAO memberDao) {
		this.memberDao = memberDao;
	}

	
	/**************************** Authentication ****************************/
	
	private PasswordEncoder passwordEncoder;
	
	public void setPasswordEncoder(PasswordEncoder passwordEncoder){
		this.passwordEncoder = passwordEncoder;
	}
	
	
	private SaltSource saltSource;

	public void setSaltSource(SaltSource saltSorurce) {
		this.saltSource = saltSorurce;
	}

	
	/****************************** Validation ******************************/
	
	private Validator validator;
	
	public boolean validate(Member member, MessageContext context, String password1, String password2){
		boolean valid = validator.validate(member, context);
		
		Member user = null;
		try{
			user = findByUsername(member.getUsername());
		}catch(UsernameNotFoundException e){
			//The username isn't registered yet, so we just do nothing
		}
		if((user != null) && (user.getId() != member.getId())){
			context.addMessage((new MessageBuilder()).error().code("error_existing_username").build());
			valid = false;
		}
		
		if((password1 != null || password2 != null) && !password1.equals(password2)){
			context.addMessage((new MessageBuilder()).error().code("error_different_passwords").build());
			valid = false;
		}
			
		return valid;
	}
	
	public Validator getValidator() {
		return validator;
	}

	public void setValidator(Validator validator) {
		this.validator = validator;
	}
}
